package com.ndp.fb.mq.service.publishhandleservice.pre;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.ndp.fb.constant.CommonConstants;
import com.ndp.fb.exception.ErrorCode;
import com.ndp.fb.exception.VideoUploadException;
import com.ndp.fb.fbagent.service.AdResouceFBService;
import com.ndp.fb.model.business.video.PreVideoUploadProgress;
import com.ndp.fb.model.fbagent.param.APIPriority;
import com.ndp.fb.model.fbagent.param.ApiCallBackTarget;
import com.ndp.fb.model.fbagent.param.adcreative.ObjectStorySpec;
import com.ndp.fb.model.fbagent.param.advideo.AdVideoParam;
import com.ndp.fb.mongo.api.AdVideoMongoService;
import com.ndp.fb.mongo.api.PublishCacheMongoService;
import com.ndp.fb.mongo.model.AdvideoAccountForMongo;
import com.ndp.fb.mongo.model.AdvideoForMongo;
import com.ndp.fb.mq.api.service.publishhandleservice.PublishPreProcessor;
import com.ndp.fb.mq.service.schedulerservice.adpublish.PublishCacheMongoServiceFailover;
import com.ndp.fb.redis.api.RedisClient;
import com.ndp.fb.redis.model.AdCreativeLevel;
import com.ndp.fb.redis.model.AdPublishCache;
import com.ndp.fb.util.DateFormatUtil;
import com.ndp.fb.util.ListUtil;
import com.ndp.fb.util.StringUtil;

/**
 * 广告发布前，检查是否是视频广告
 * 如果是视频广告，检查要发布的视频在账号中是否已经存在
 * 如果存在，在直接刷新 cache 中的 vide 的id
 * 如果不存在，需要上传完视频后，刷新videoId到 cache 中
 * 并保存 fb account - md5--videoId的对应该关系
 * Created by hunk.qin on 2016/1/14.
 */
@Component
public class PublishPreVideoUploadImp implements PublishPreProcessor {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    public static final String START = "start";


    @Reference
    private AdVideoMongoService adVideoMongoService;

    @Reference
    private AdResouceFBService adResouceFBService;

    @Autowired
    private PublishCacheMongoServiceFailover publishCacheMongoService;

    @Autowired
    public RedisClient redisClient;


    @Override
    public String refreshCache(String batchIdkey,Long batchId,AdPublishCache cache )  {
      String result= CommonConstants.TRUE;
      Map<String,String> uploadVideoStatus=new HashMap<>();
        try {
            Map<String, AdCreativeLevel> adCreativeLevelMap = JSON.parseObject(publishCacheMongoService.findCacheBlockByType(batchId,
                    PublishCacheMongoService.CREATIVE), new TypeReference<HashMap<String,AdCreativeLevel>>(){});
          boolean isVideoAllReady=true;
          for (Map.Entry<String, AdCreativeLevel> creativeLevelEntry : adCreativeLevelMap.entrySet()){
              AdCreativeLevel adCreativeLevel =creativeLevelEntry.getValue();
              ObjectStorySpec objectStorySpec = adCreativeLevel.getObjectStorySpec();
              String videomd5 = objectStorySpec.getVideoData().getMd5();
              if(objectStorySpec.getVideoData()!=null&& StringUtil.isNotEmpty(videomd5)){
                  // 先检查该 md5 对应的账号是否已经上传过如果已经传过，则直接刷新到cache里面，如果没有上传
                  List<AdvideoForMongo> videolist = adVideoMongoService.findByMd5(videomd5);
                  if(ListUtil.isEmpty(videolist)){
                      //唯一的可能就是 mongo出错了，查了个空list 出来了，但是这种情况不应该存在的，解决mongo问题后，可删除
                      Thread.sleep(10*1000);
                      videolist = adVideoMongoService.findByMd5(videomd5);
                  }

                  Long  videoIdOnFbAct=null;
                  String actAccountId = adCreativeLevel.getActAccountId();
                  videoIdOnFbAct = getVideoIdOnAct(videolist, videoIdOnFbAct, actAccountId);
                  if (videoIdOnFbAct!=null){
                      publishCacheMongoService.updateCreativeVideoIdByKkey(batchIdkey,creativeLevelEntry.getKey(),String.valueOf(videoIdOnFbAct));
                  }else{
                      startUploadVideo(uploadVideoStatus, adCreativeLevel, videomd5,batchIdkey, videolist, actAccountId);
                      isVideoAllReady=false;

                  }
              }
          }

          if(isVideoAllReady){
              logger.info("all video all reday");
          }else{
              //多线程开始传输视频到不同的账号，开始进行状态完成的检查，每 3秒检查一次，最长检查1个小时，如果还都未完成，本前置作业失败
              checkUpload(uploadVideoStatus);
              //完成后对creative set id
              for (Map.Entry<String, AdCreativeLevel> creativeLevelEntry : adCreativeLevelMap.entrySet()){
                  AdCreativeLevel adCreativeLevel =creativeLevelEntry.getValue();
                  ObjectStorySpec objectStorySpec = adCreativeLevel.getObjectStorySpec();
                  String videomd5 = objectStorySpec.getVideoData().getMd5();
                  List<AdvideoForMongo> videolist = adVideoMongoService.findByMd5(videomd5);
                  Long  videoIdOnFbAct=null;
                  String actAccountId = adCreativeLevel.getActAccountId();
                  videoIdOnFbAct = getVideoIdOnAct(videolist, videoIdOnFbAct, actAccountId);
                  if (videoIdOnFbAct!=null){
                      publishCacheMongoService.updateCreativeVideoIdByKkey(batchIdkey,creativeLevelEntry.getKey(),String.valueOf(videoIdOnFbAct));
                  }else{
                      throw new VideoUploadException(ErrorCode.ERROR_20011,adCreativeLevel.getFbAccountId().toString());
                  }
              }
          }
      }catch (Exception e){
          result=e.getMessage();
          logger.error(e.getMessage(),e);
      }finally {
          return result;
      }
    }

    private Long getVideoIdOnAct(List<AdvideoForMongo> videolist, Long videoIdOnFbAct, String actAccountId) {
        for (AdvideoForMongo advideoForMongo : videolist) {
            List<AdvideoAccountForMongo> uploadFbAccounts = advideoForMongo.getUploadFbAccounts();
            if(ListUtil.isNotEmpty(uploadFbAccounts)){
                for (AdvideoAccountForMongo uploadFbAccount : uploadFbAccounts) {
                    if(("act_"+uploadFbAccount.getFbAccountId()).equals(actAccountId)){
                        videoIdOnFbAct=uploadFbAccount.getFbVideoId();
                    }
                }
            }
        }
        return videoIdOnFbAct;
    }

    private void checkUpload(Map<String, String> uploadVideoStatus) throws Exception {
        int checksum=0;
        logger.info("-------------video upload progress size :{}-----------------",uploadVideoStatus.size());
        while (uploadVideoStatus.size()>0&&checksum<1200){
            for (Map.Entry<String, String> entry : uploadVideoStatus.entrySet()) {
                 PreVideoUploadProgress runProgress = (PreVideoUploadProgress)redisClient.get(entry.getKey());
                 if(runProgress==null){
                     uploadVideoStatus.put(entry.getKey(), PreVideoUploadProgress.SUCCEED);
                 }else{
                     switch (runProgress.getStatus()) {
                         case PreVideoUploadProgress.SUCCEED:
                             uploadVideoStatus.put(entry.getKey(), PreVideoUploadProgress.SUCCEED);
                             logger.info("-------------update done  key is  to  :{}-----------------",entry.getKey());
                             break;
                         case PreVideoUploadProgress.RUNING:
                             logger.info("{},下的时视频上传中,文件分块进度：{}/{}", entry.getKey(), runProgress.getCurrentBlock(),runProgress.getCountBlock());
                             break;
                         case PreVideoUploadProgress.FAIL:
                             throw new VideoUploadException(runProgress.getErrormsg());

                     }
                 }
            }
            //如果都没有异常，删除已经完成刷新的creative 不在监控
            Iterator<Map.Entry<String,String>> it = uploadVideoStatus.entrySet().iterator();
            while(it.hasNext()){
                Map.Entry<String,String> entry = it.next();
                if(entry.getValue().equals(PreVideoUploadProgress.SUCCEED)){
                    it.remove();
                }
            }
            //sleep 3 秒
            checksum++;
            Thread.sleep(1000*3);
        }
        if(checksum>=1200)
            throw new VideoUploadException(ErrorCode.ERROR_20004);
        //刚上传完成的视频，Facebook ready ,等 120秒后在用
        for (int i=0;i<40;i++) {
            Thread.sleep(3*1000);
            logger.info("Video not ready for use in ad creative , wait :{},second",i*3);
        }

    }

    private void startUploadVideo(Map<String, String> uploadVideoStatus,
                                  AdCreativeLevel adCreativeLevel, String videomd5,String batchid, List<AdvideoForMongo> videolist, String actAccountId) throws Exception {

        AdVideoParam requestParam = new AdVideoParam();
        Long account=Long.valueOf(actAccountId.substring(4));
        requestParam.setFbAccountId(account);

        String key=videomd5+"_"+account+"_"+batchid;

        String accessToken = adCreativeLevel.getAccessToken();
        requestParam.setAccessToken(accessToken);
        String nfsId = videolist.get(0).getNfsId();
        requestParam.setNfsId(nfsId);
        int fileSize = videolist.get(0).getSize().intValue();

        requestParam.setFileSize(fileSize);
        requestParam.setApiPriorityCode(APIPriority.UploadVideoAsyn.getCode());


        Map<String, String> equipment = new HashMap<>();

        equipment.put("token", accessToken);
        equipment.put("accountId", String.valueOf(account));
        equipment.put("nfsId", nfsId);
        equipment.put("fileSize", fileSize + "");
        equipment.put("md5",videomd5);
        equipment.put("progresskey",key);
        requestParam.setEquipment(equipment);
        requestParam.setUploadPhase(START);
        requestParam.setApiCallbackTargetType(ApiCallBackTarget.PublishResource.StartUploadAdVideo.getTargetType());
        requestParam.setCallBack(true);


        uploadVideoStatus.put(key, START);
        //如果 redise 里面已经有在该视频在该账号下上传的任务，则只检查是否完成，不在重传,如果已经失败了，则删除失败的进度，重来
        Object object = redisClient.get(key);
        if(object!=null){
            PreVideoUploadProgress progress=(PreVideoUploadProgress)object;
            if(progress.getStatus().equals(PreVideoUploadProgress.FAIL)){
                redisClient.delete(key);
            }else{
                logger.info("videomd5:{},accountId:{},的视频已经在传输中,不再次上传，只检查状态",videomd5,account);
            }
        }
        if(!redisClient.hasKey(key)) {
            adResouceFBService.startUploadAdVideo(requestParam);
            PreVideoUploadProgress progress=new PreVideoUploadProgress();
            progress.setStatus(PreVideoUploadProgress.START);
            progress.setUpdateTime(DateFormatUtil.formatLong19(new Date()));
            redisClient.set(key,progress,1, TimeUnit.HOURS.toString());
        }
    }

    @Override
    public boolean check( AdPublishCache cache) {
        Map<String, AdCreativeLevel> adCreativeLevelMap = cache.getAdCreativeLevelMap();
        boolean result=false;
        for (AdCreativeLevel adCreativeLevel : adCreativeLevelMap.values()) {
            ObjectStorySpec objectStorySpec = adCreativeLevel.getObjectStorySpec();
            if(objectStorySpec.getVideoData()!=null&& StringUtil.isNotEmpty(objectStorySpec.getVideoData().getMd5())){
                result=true;
                break;
            }
        }
        return result;
    }

}


